﻿Imports System.Runtime.CompilerServices
Imports EDMDesigner.Business
Imports EDMDesigner.Business.CSDL.Type
Imports EDMDesigner.Business.MSL.EntityType
Imports EDMDesigner.Business.MSL.Condition
Imports EDMDesigner.Business.MSL.CUDFunction

Imports <xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS">

Friend Module WriterMSL
    Friend Function WriteMSL(ByVal edm As EDM) As XElement
        Dim csdl = edm.CSDLContainer
        Dim csdlNS = csdl.Namespace & "."
        Return <Mapping Space="C-S" xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS">
                   <EntityContainerMapping StorageEntityContainer=<%= edm.SSDLContainer.Name %> CdmEntityContainer=<%= csdl.Name %>>
                       <%= From entitySet In csdl.EntitySets _
                           Let entityTypes = (From entityType In csdl.EntityTypes _
                           Where entityType.EntitySetName Is entitySet.EntitySetName) _
                           Select <EntitySetMapping Name=<%= entitySet.EntitySetName %>>
                                      <%= From entityType In entityTypes _
                                          Let Mapping = entitySet.Mapping _
                                          Select <EntityTypeMapping TypeName=<%= String.Format("IsTypeOf({0}{1})", csdlNS, entityType.Name) %>>
                                                     <%= From table In entityType.Mapping.MappedSSDLTables _
                                                         Select <MappingFragment StoreEntitySet=<%= table.EntitySetName %>>
                                                                    <%= From scalarMapping In entityType.Mapping.GetSpecificMappingForTable(table) _
                                                                        Select <ScalarProperty Name=<%= scalarMapping.Property.Name %> ColumnName=<%= scalarMapping.Column.Name %>/> %>
                                                                    <%= MappingComplexProperties(entityType, entityType.Mapping, table, csdlNS) %>
                                                                    <%= From condition In entityType.Mapping.ConditionsMapping _
                                                                        Where condition.Table Is table _
                                                                        Let element = <Condition/> _
                                                                        Let columnCondition = TryCast(condition, ColumnConditionMapping) _
                                                                        Let propertyCondition = TryCast(condition, PropertyConditionMapping) _
                                                                        Select element.AddAttribute("ColumnName", If(columnCondition Is Nothing, Nothing, columnCondition.Column.Name)).AddAttribute("Name", If(propertyCondition Is Nothing, Nothing, propertyCondition.CSDLProperty.Name)).AddMappingConditionAttribute(condition) %>
                                                                </MappingFragment> %>
                                                 </EntityTypeMapping> %>
                                      <%= From entityType In entityTypes _
                                          Select CUDFunctionMapping(entityType, csdlNS, String.Concat(edm.SSDLContainer.Namespace, ".")) %>
                                  </EntitySetMapping> %>
                       <%= From association In csdl.Associations _
                           Where association.Mapping.SSDLTableMapped IsNot Nothing _
                           Select <AssociationSetMapping Name=<%= association.AssociationSetName %> TypeName=<%= String.Concat(csdlNS, association.Name) %> StoreEntitySet=<%= association.Mapping.SSDLTableMapped.Name %>>
                                      <EndProperty Name=<%= association.PropertyEnd1Role %>>
                                          <%= From navigationPropertyMapping In association.PropertyEnd1.Mapping _
                                              Select <ScalarProperty Name=<%= navigationPropertyMapping.Property.Name %> ColumnName=<%= navigationPropertyMapping.Column.Name %>/> %>
                                      </EndProperty>
                                      <EndProperty Name=<%= association.PropertyEnd2Role %>>
                                          <%= From navigationPropertyMapping In association.PropertyEnd2.Mapping _
                                              Select <ScalarProperty Name=<%= navigationPropertyMapping.Property.Name %> ColumnName=<%= navigationPropertyMapping.Column.Name %>/> %>
                                      </EndProperty>
                                      <%= From condition In association.Mapping.ConditionsMapping _
                                          Select MappingColumnCondition(condition) %>
                                  </AssociationSetMapping> %>
                       <%= From funct In csdl.Functions _
                           Select <FunctionImportMapping FunctionImportName=<%= funct.Name %> FunctionName=<%= String.Format("{0}.{1}", edm.SSDLContainer.Namespace, funct.SSDLFunction.Name) %>/> %>
                   </EntityContainerMapping>
               </Mapping>
    End Function

    Private Function MappingComplexProperties(ByVal type As TypeBase, ByVal mapping As MappingBase, ByVal table As SSDL.EntityType.EntityType, ByVal csdlNS As String) As IEnumerable(Of XElement)
        Return From complexProperty In type.AllComplexProperties _
               Let complexPropertyMapping = mapping.GetEntityTypeSpecificComplexPropertyMapping(complexProperty) _
               Where complexPropertyMapping IsNot Nothing _
               Let complexType = complexProperty.ComplexType _
               Select <ComplexProperty Name=<%= complexProperty.Name %>>
                          <%= From scalarMapping In complexPropertyMapping.GetSpecificMappingForTable(table) _
                              Select <ScalarProperty Name=<%= scalarMapping.Property.Name %> ColumnName=<%= scalarMapping.Column.Name %>/> %>
                          <%= From subComplexProperty In complexType.ComplexProperties _
                              Select MappingComplexProperties(complexType, complexPropertyMapping, table, csdlNS) %>
                      </ComplexProperty>
    End Function

    Private Function MappingColumnCondition(ByVal condition As ColumnConditionMapping) As XElement
        Return <Condition ColumnName=<%= condition.Column.Name %>/>.AddMappingConditionAttribute(condition)
    End Function
    <Extension()> _
    Private Function AddMappingConditionAttribute(ByVal element As XElement, ByVal conditionMapping As ConditionMapping) As XElement
        Select Case conditionMapping.Operator
            Case ConditionOperator.IsNotNull
                element.Add(New XAttribute("IsNull", False))
                Exit Select
            Case ConditionOperator.IsNull
                element.Add(New XAttribute("IsNull", True))
                Exit Select
            Case ConditionOperator.Equals
                element.Add(New XAttribute("Value", conditionMapping.Value))
                Exit Select
        End Select
        Return element
    End Function

    Private Function CUDFunctionMapping(ByVal entityType As CSDL.Type.Entity.EntityType, ByVal csdlNS As String, ByVal ssdlNS As String) As XElement
        Dim mapping = entityType.Mapping
        If mapping.InsertFunctionMapping Is Nothing OrElse mapping.UpdateFunctionMapping Is Nothing OrElse mapping.DeleteFunctionMapping Is Nothing Then
            Return Nothing
        End If
        Dim modificationFunctionMapping = <ModificationFunctionMapping/>
        Dim insertFunction = mapping.InsertFunctionMapping
        If insertFunction IsNot Nothing Then
            modificationFunctionMapping.Add(<InsertFunction FunctionName=<%= String.Concat(ssdlNS, insertFunction.SSDLFunction.Name) %>>
                                                <%= CUDFunctionMappingAssociation(insertFunction) %>
                                                <%= CUDFunctionMappingParameters(insertFunction) %>
                                                <%= CUDFunctionMappingResults(insertFunction) %>
                                            </InsertFunction>)
        End If
        Dim updateFunction = mapping.UpdateFunctionMapping
        If updateFunction IsNot Nothing Then
            modificationFunctionMapping.Add(<UpdateFunction FunctionName=<%= String.Concat(ssdlNS, updateFunction.SSDLFunction.Name) %>>
                                                <%= CUDFunctionMappingAssociation(updateFunction) %>
                                                <%= CUDFunctionMappingParameters(updateFunction) %>
                                                <%= CUDFunctionMappingResults(updateFunction) %>
                                            </UpdateFunction>)
        End If
        Dim deleteFunction = mapping.DeleteFunctionMapping
        If deleteFunction IsNot Nothing Then
            modificationFunctionMapping.Add(<DeleteFunction FunctionName=<%= String.Concat(ssdlNS, deleteFunction.SSDLFunction.Name) %>>
                                                <%= CUDFunctionMappingAssociation(deleteFunction) %>
                                                <%= CUDFunctionMappingParameters(deleteFunction) %>
                                                <%= CUDFunctionMappingResults(deleteFunction) %>
                                            </DeleteFunction>)
        End If
        Return <EntityTypeMapping TypeName=<%= String.Concat(csdlNS, entityType.Name) %>>
                   <%= modificationFunctionMapping %>
               </EntityTypeMapping>
    End Function
    Private Function CUDFunctionMappingAssociation(ByVal functionMapping As CUDFunctionMapping) As IEnumerable(Of XElement)
        Return From cudFunctionAssiociation In functionMapping.AssociationMappings _
               Select <AssociationEnd AssociationSet=<%= cudFunctionAssiociation.Association.AssociationSetName %> From=<%= cudFunctionAssiociation.FromRole %> To=<%= cudFunctionAssiociation.ToRole %>>
                          <%= CUDFunctionMappingScalarPropertiesParameters(cudFunctionAssiociation.AssociationPropertiesMapping) %>
                      </AssociationEnd>
    End Function
    Private Function CUDFunctionMappingParameters(ByVal functionMapping As CUDFunctionMapping) As IEnumerable(Of XElement)
        Return CUDFunctionMappingParameters(functionMapping.ParametersMapping)
    End Function
    Private Function CUDFunctionMappingParameters(ByVal cudFunctionParameters As EntityTypeCUDFunctionParametersMapping) As IEnumerable(Of XElement)
        Return CUDFunctionMappingScalarPropertiesParameters(cudFunctionParameters).Union( _
                From complexParameter In cudFunctionParameters.ComplexPropertiesMapping _
                Select <Complexproperty Name=<%= complexParameter.Key.Name %>>
                           <%= CUDFunctionMappingParameters(complexParameter.Value) %>
                       </Complexproperty> _
                )
    End Function
    Private Function CUDFunctionMappingScalarPropertiesParameters(ByVal cudFunctionParameters As CUDFunctionParametersMapping) As IEnumerable(Of XElement)
        Return From parameter In cudFunctionParameters _
                Select <ScalarProperty Name=<%= parameter.Key.Name %> ParameterName=<%= parameter.Value.SSDLFunctionParameter.Name %>/> _
            .AddAttribute(Nothing, "Version", parameter.Value.Version)
    End Function
    Private Function CUDFunctionMappingResults(ByVal functionMapping As CUDFunctionMapping) As IEnumerable(Of XElement)
        Return From result In functionMapping.ResultsMapping _
               Select <ResultBinding Name=<%= result.Key.Name %> ColumnName=<%= result.Value %>/>
    End Function
End Module
